package net.java.xpath.completion; import java.io.IOException; import java.util.HashSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.StyledDocument; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import net.java.xpath.ui.XPathEvaluator; import net.java.xpath.ui.XPathTopComponent; import org.netbeans.api.editor.completion.Completion; import org.netbeans.spi.editor.completion.CompletionProvider; import org.netbeans.spi.editor.completion.CompletionResultSet; import org.netbeans.spi.editor.completion.CompletionTask; import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery; import org.netbeans.spi.editor.completion.support.AsyncCompletionTask; import org.openide.util.Exceptions; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * * @author Michael Bien */ public class XPathCompletionProvider implements CompletionProvider { public CompletionTask createTask(int i, JTextComponent textComponent) { if (i != CompletionProvider.COMPLETION_QUERY_TYPE) { return null; } return new AsyncCompletionTask(new XPathCompletionQuery(), textComponent); } public int getAutoQueryTypes(JTextComponent arg0, String arg1) { return 0; } private static class XPathCompletionQuery extends AsyncCompletionQuery { @Override protected void query(CompletionResultSet completionResultSet, Document document, int caretOffset) { try { final StyledDocument bDoc = (StyledDocument) document; String lineTilCaret = bDoc.getText(0, caretOffset); String lineTilCaretTrimed = lineTilCaret.trim(); String exp; String filterToken; boolean attributeQuery; int slashIndex = lineTilCaret.lastIndexOf('/'); int atIndex = lineTilCaret.lastIndexOf('@'); int dotOffset; if(lineTilCaretTrimed.length() >= 2 && lineTilCaretTrimed.charAt(0) == '/' && lineTilCaretTrimed.charAt(1) == '/' && lineTilCaretTrimed.lastIndexOf('/') == 1) { exp = lineTilCaret.substring(0, slashIndex+1) + "*"; dotOffset = caretOffset - lineTilCaret.length() + exp.length()-1; }else if(lineTilCaretTrimed.length() >= 1 && lineTilCaretTrimed.charAt(0) == '/' && lineTilCaretTrimed.lastIndexOf('/') == 0) { exp = lineTilCaret.substring(0, slashIndex+1); dotOffset = caretOffset - lineTilCaret.length() + exp.length(); }else if(slashIndex != -1 || atIndex != -1) { exp = lineTilCaret.substring(0, Math.max(atIndex-1, slashIndex)); dotOffset = caretOffset - lineTilCaret.length() + exp.length()+1; } else{ exp = "/"; dotOffset = 0; } attributeQuery = atIndex > slashIndex; filterToken = lineTilCaret.substring(Math.max(slashIndex, atIndex)+1, caretOffset); XPathEvaluator eval = new XPathEvaluator(); try { final String xml = XPathTopComponent.getDefault().lastFocusedEditor.getText(); NodeList list = (NodeList)eval.evaluate(exp, xml, XPathConstants.NODESET); if(list != null) { HashSet<String> set = new HashSet<String>(); for(int b = 0; b < list.getLength(); b++) { Node current = list.item(b); if(attributeQuery) { NamedNodeMap attributes = current.getAttributes(); for(int a = 0; a < attributes.getLength(); a++) { String name = attributes.item(a).getNodeName(); if(name != null && name.startsWith(filterToken)) { set.add("@"+name); } } }else{ NodeList childNodes = current.getChildNodes(); for(int n = 0; n < childNodes.getLength(); n++) { Node item = childNodes.item(n); String name = item.getNodeName(); if(item.getNodeType() == Node.ELEMENT_NODE && name.startsWith(filterToken)) { set.add(name); } } } } for (String item : set) { completionResultSet.addItem( new XPathCompletionItem( item, dotOffset, caretOffset ) ); } }else{ Completion.get().hideAll(); } } catch (SAXException ex) { } catch (IOException ex) { } catch (XPathExpressionException ex) { } } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } completionResultSet.finish(); } } }